package com.fecean.aabbc.netty.client.prod7;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.concurrent.DefaultThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import java.util.concurrent.ThreadFactory;

/**
 ** Netty客户端配置类
 *
 * @author: chenfenghai
 * @version: 1.0
 * @date: 2020-03-05 16:21
 * @blame Team
 */
@Configuration
public class NettyClientConfiguration {

    /**
     ** Netty客户端通道初始化
     */
    @Resource
    NettyClientChannelInitializer nettyClientChannelInitializer;

    /**
     ** Netty客户端Bootstrap Bean通用注册设置
     *
     * @return Bootstrap
     */
    @Bean
    public Bootstrap bootstrap(){
        // new 一个工作线程组，并定义线程名称，方便在日志中查看
        ThreadFactory workerFactory = new DefaultThreadFactory("n.c.work");
        EventLoopGroup workGroup = new NioEventLoopGroup(workerFactory);
        return new Bootstrap()
                // 绑定工作线程
                .group(workGroup)
                // 注册NioSocketChannel通道
                .channel(NioSocketChannel.class)
                // 在TCP/IP协议中，无论发送多少数据，总是要在数据前面加上协议头，同时，对方接收到数据，也需要发送ACK表示确认。
                // 为了尽可能的利用网络带宽，TCP总是希望尽可能的发送足够大的数据。
                // 这里就涉及到一个名为Nagle的算法，该算法的目的就是为了尽可能发送大块数据，避免网络中充斥着许多小数据块。
                // TCP_NODELAY就是用于启用或关于Nagle算法。
                // 如果要求高实时性，有数据发送时就马上发送，就将该选项设置为true关闭Nagle算法；
                // 如果要减少发送次数减少网络交互，就设置为false等累积一定大小后再发送。默认为false。
                .option(ChannelOption.TCP_NODELAY, true)
                // SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口，即使以前建立的将此端口用做他们的本地端口的连接仍存在。
                // 这通常是重启监听服务器时出现，若不设置此选项，则bind时将出错。
                // SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例，只要每个实例捆绑一个不同的本地IP地址即可。
                // 对于TCP，我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。
                // SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上，只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。
                // SO_REUSEADDR允许完全重复的捆绑：当一个IP地址和端口绑定到某个套接口上时，还允许此IP地址和端口捆绑到另一个套接口上。
                // 一般来说，这个特性仅在支持多播的系统上才有，而且只对UDP套接口而言（TCP不支持多播）
                .option(ChannelOption.SO_REUSEADDR, true)
                // 添加handler，监听Channel的各种动作以及状态的改变，包括连接，绑定，接收消息等
                // handler在初始化时就会执行
                .handler(nettyClientChannelInitializer);
    }
}
